﻿#include "ElementBase.h"
#include <qdir.h>
#include <qpainter.h>
#include <QStyleOptionGraphicsItem>
#include <QGraphicsSceneMouseEvent>
#include <QJsonValue>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
///////////////////////////////////////////////
/// ElementBasePrivate
///////////////////////////////////////////////
struct ElementBasePrivate{
    static void initElementBase(ElementBase* elementBase);
    static void drawSelectedBorder(ElementBase *elementBase,QPainter *painter);
    static void rotateRect(ElementBase *elementBase,const QPointF &mousePos);
    static void setShowRect(ElementBase *elementBase,qreal w, qreal h);
};

void ElementBasePrivate::initElementBase(ElementBase *elementBase)
{
    elementBase->setFlags(ElementBase::ItemIsMovable|ElementBase::ItemIsSelectable);
    // 接收鼠标悬浮事件
    elementBase->setAcceptHoverEvents(true);
    //elementBase->mPrivate = new ElementBasePrivate(elementBase);
}

void ElementBasePrivate::drawSelectedBorder(ElementBase *elementBase,QPainter *painter)
{
    QPen oldPen = painter->pen();
    QBrush oldBrush = painter->brush();
    QPen pen;
    pen.setWidth(1);
    pen.setBrush(elementBase->mqcSelectedBorderColor);
    pen.setCapStyle(Qt::RoundCap);
    pen.setJoinStyle(Qt::RoundJoin);
    pen.setStyle(Qt::DotLine);
    painter->setPen(pen);
    painter->setBrush(elementBase->mqcSelectedBorderColor);

    qreal radius=elementBase->mdCircleRadius;

    qreal xLeft = elementBase->mShowRect.x() -radius;
    qreal xMid = elementBase->mShowRect.x()+elementBase->mShowRect.width()/2;
    qreal xRight = elementBase->mShowRect.right()+radius;
    qreal yTop = elementBase->mShowRect.y()-radius;
    qreal yMid = elementBase->mShowRect.y()+elementBase->mShowRect.height()/2;
    qreal yBottom = elementBase->mShowRect.bottom()+radius;
    // topleft
    painter->drawEllipse(QPointF(xLeft,yTop),radius,radius);
    // topmid
    painter->drawEllipse(QPointF(xMid,yTop),radius,radius);
    // topright
    painter->drawEllipse(QPointF(xRight,yTop),radius,radius);
    // midright
    painter->drawEllipse(QPointF(xRight,yMid),radius,radius);
    // bottomright
    painter->drawEllipse(QPointF(xRight,yBottom),radius,radius);
    // bottommid
    painter->drawEllipse(QPointF(xMid,yBottom),radius,radius);
    // bottomleft
    painter->drawEllipse(QPointF(xLeft,yBottom),radius,radius);
    // midmleft
    painter->drawEllipse(QPointF(xLeft,yMid),radius,radius);

    // // 旋转点
    // painter->drawEllipse(QPointF(xMid,yTop-elementBase->mdLineLen),radius,radius);
    //painter->drawLine(QPointF(xMid, yTop),QPointF(xMid, yTop-elementBase->mdLineLen));

    painter->setBrush(oldBrush);
    painter->drawRect(
        QRectF(elementBase->mShowRect.x() -radius,elementBase->mShowRect.y()-radius,
               elementBase->mShowRect.width()+2*radius,elementBase->mShowRect.height()+2*radius)
        );
    //painter->drawLine();
    painter->setPen(oldPen);
}

void ElementBasePrivate::rotateRect(ElementBase *elementBase, const QPointF &mousePos)
{
    // // 设置中心点为原点
    // QPointF originPos = {0.,0.};
    // // 从原点延伸出去两条线，鼠标按下时的点和当前鼠标位置所在点的连线
    // QLineF p1 = QLineF(originPos, elementBase->mPressPos);
    // QLineF p2 = QLineF(originPos, mousePos);
    // // 旋转角度
    // qreal dRotateAngle = p2.angleTo(p1);

    // // 设置旋转中心
    // elementBase->setTransformOriginPoint(originPos);

    // // 计算当前旋转的角度
    // qreal dCurAngle = elementBase->rotation() + dRotateAngle;
    // while (dCurAngle > 360.0) {
    //     dCurAngle -= 360.0;
    // }

    // // 设置旋转角度
    // elementBase->setRotation(dCurAngle);

    // // 刷新显示
    // elementBase->update();
}

void ElementBasePrivate::setShowRect(ElementBase *elementBase, qreal w, qreal h)
{
    if(w<0)w=0;
    if(h<0)h=0;
    elementBase->mShowRect.setX(-w/2);
    elementBase->mShowRect.setY(-h/2);
    elementBase->mShowRect.setWidth(w);
    elementBase->mShowRect.setHeight(h);
}

/*
enum GraphicsItemFlag {
    ItemIsMovable = 0x1,
    ItemIsSelectable = 0x2,
    ItemIsFocusable = 0x4,
    ItemClipsToShape = 0x8,
    ItemClipsChildrenToShape = 0x10,
    ItemIgnoresTransformations = 0x20,
    ItemIgnoresParentOpacity = 0x40,
    ItemDoesntPropagateOpacityToChildren = 0x80,
    ItemStacksBehindParent = 0x100,
    ItemUsesExtendedStyleOption = 0x200,
    ItemHasNoContents = 0x400,
    ItemSendsGeometryChanges = 0x800,
    ItemAcceptsInputMethod = 0x1000,
    ItemNegativeZStacksBehindParent = 0x2000,
    ItemIsPanel = 0x4000,
    ItemIsFocusScope = 0x8000, // internal
    ItemSendsScenePositionChanges = 0x10000,
    ItemStopsClickFocusPropagation = 0x20000,
    ItemStopsFocusHandling = 0x40000,
    ItemContainsChildrenInShape = 0x80000
    // NB! Don't forget to increase the d_ptr->flags bit field by 1 when adding a new flag.
};
*/
QRectF ElementBase::boundingRect() const
{
    // return mShowRect
    //     .adjusted(-2*mdCircleRadius, -2*mdCircleRadius-mdLineLen,
    //               2*mdCircleRadius, 2*mdCircleRadius);
    return mShowRect
        .adjusted(-2*mdCircleRadius, -2*mdCircleRadius,
                  2*mdCircleRadius, 2*mdCircleRadius);
}

ElementBase::ElementBase(ElementUiBase *ui)
    :mUI(ui)
{
    ElementBasePrivate::initElementBase(this);
}

ElementBase::ElementBase(QGraphicsItem *parent)
    :QGraphicsObject(parent)
{
    //ElementBasePrivate::initElementBase(this);
};

ElementBase::ElementBase(const QSizeF &rect, ElementUiBase *ui)
    :ElementBase(ui)
{
    ElementBasePrivate::setShowRect(this,rect.width(),rect.height());
}

ElementBase::~ElementBase()
{
    if(mUI)
        delete mUI;
}

void ElementBase::setShowRect(const QSizeF &size)
{
    setShowRect(size.width(),size.height());
}

void ElementBase::setShowRect(qreal w, qreal h)
{
    ElementBasePrivate::setShowRect(this,w,h);
    this->update();
}

void ElementBase::addRotation(qreal arg)
{
    // 计算当前旋转的角度
    qreal dCurAngle = rotation() + arg;
    while (dCurAngle > 360.0) {
        dCurAngle -= 360.0;
    }

    // 设置旋转角度
    setRotation(dCurAngle);

    // 刷新显示
    update();
}

void ElementBase::verticalFlip()
{
    QTransform trans;
    trans.scale(-1, 1); // 垂直翻转
    setTransform(trans,true);
}

void ElementBase::horizontalFlip()
{
    QTransform transform;
    transform.scale(1, -1); // 垂直翻转
    setTransform(transform,true);
}

void ElementBase::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{

    painter->save();
    if(option->state&QStyle::State_Selected)
    {
        ElementBasePrivate::drawSelectedBorder(this,painter);
    }else if(mIsContains){
        for(const QPointF& p:mUI->mPoints){
            drawHSpots(
                painter,
                QRectF(
                    mShowRect.x()+p.x()*mShowRect.width()-this->mdCircleRadius,
                    mShowRect.y()+p.y()*mShowRect.height()-this->mdCircleRadius,
                    this->mdCircleRadius*2,
                    this->mdCircleRadius*2
                    )
                );
        }
    }
    this->draw(painter,this->mShowRect);
    painter->restore();
}

void ElementBase::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    QPointF pos = event->pos();
    if(mShowRect.contains(mPressPos)){
        QGraphicsItem::mouseMoveEvent(event);
    }
    else{
        QRectF rect{-mdCircleRadius, mShowRect.y()-mdLineLen-3.*mdCircleRadius,3.*mdCircleRadius,3.*mdCircleRadius};
        if(rect.contains(mPressPos)){
            ElementBasePrivate::rotateRect(this,pos);
        }
    }
}

void ElementBase::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        this->mPressPos=event->pos();
        setZValue(1); // 把这个项提升到最前面
    }
    QGraphicsObject::mousePressEvent(event);
    emit clicked();
}

void ElementBase::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    emit clicked();
    QGraphicsObject::mouseReleaseEvent(event);
}


void ElementBase::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
    setCursor(Qt::PointingHandCursor); // 改变鼠标光标
    mIsContains=true;
    update(); // 通知重绘
}

// void ElementBase::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
// {
//     QPainter painter;
//     for(const QPointF& p:mUI->mPoints){
//         drawHSpots(
//             &painter,
//             QRectF(
//                 mShowRect.x()+p.x()*mShowRect.width(),
//                 mShowRect.y()+p.y()*mShowRect.height(),
//                 this->mdCircleRadius,
//                 this->mdCircleRadius
//                 )
//             );
//     }
// }

void ElementBase::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
    setCursor(Qt::ArrowCursor); // 恢复默认光标
    mIsContains=false;
    update(); // 通知重绘
}

void ElementBase::draw(QPainter *painter, const QRectF &showRect) const
{
    if(!mUI)
    {
        // painter->drawRect(showRect);
        return;
    }

    for(QLineF& line:mUI->mLines){
        painter->drawLine(
            mShowRect.x()+mShowRect.width()*line.x1(),
            mShowRect.y()+mShowRect.height()*line.y1(),
            mShowRect.x()+mShowRect.width()*line.x2(),
            mShowRect.y()+mShowRect.height()*line.y2()
            );
    }
    for(QRectF& rect:mUI->mRects){
        painter->drawRect(
            mShowRect.x()+mShowRect.width()*rect.x(),
            mShowRect.y()+mShowRect.height()*rect.y(),
            mShowRect.width()*rect.width(),
            mShowRect.height()*rect.height()
            );
    }
    for(QRectF &rect:mUI->mEllipses){
        painter->drawEllipse(
            mShowRect.x()+mShowRect.width()*rect.x(),
            mShowRect.y()+mShowRect.height()*rect.y(),
            mShowRect.width()*rect.width(),
            mShowRect.height()*rect.height()
            );
    }
}

void ElementBase::drawHSpots(QPainter *painter, const QRectF &showRect) const
{
    QPen old=painter->pen();
    painter->setPen(mqcSelectedBorderColor);
    painter->drawLines(QList<QPointF>{
        showRect.topLeft(),
        showRect.bottomRight(),
        showRect.topRight(),
        showRect.bottomLeft()
    });
    painter->setPen(old);
}

QPainterPath ElementBase::shape() const
{
    QPainterPath path;
    path.addRect(mShowRect);
    if(isSelected()){
        qreal xMid = mShowRect.x()+mShowRect.width()/2;
        qreal yTop = mShowRect.y()-mdCircleRadius;
        path.addEllipse(QPointF(xMid,yTop-mdLineLen),mdCircleRadius,mdCircleRadius);
    }
    return path;
}

/////////////////////////////////////////////////////////////
/// ElementUiBase
/////////////////////////////////////////////////////////////

bool ElementBase::ElementUiBase::fromJson(const QByteArray &json)
{
    QJsonDocument  root = QJsonDocument::fromJson(json);
    QJsonObject obj = root.object();
    // lines
    QJsonArray lines = obj["lines"].toArray();
    for(int i=0;i<lines.count();++i){
        QJsonArray line = lines[i].toArray();
        this->mLines.push_back(
            QLineF(
                QPointF(line[0].toDouble(),line[1].toDouble()),
                QPointF(line[2].toDouble(),line[3].toDouble())
                )
            );
    }
    // rectangles
    QJsonArray rectangles = obj["rectangles"].toArray();
    for(int i=0;i<rectangles.count();++i){
        QJsonArray rect = rectangles[i].toArray();
        this->mRects.push_back(
            QRectF(
                rect[0].toDouble(),rect[1].toDouble(),
                rect[2].toDouble(),rect[3].toDouble()
                )
            );
    }
    // hot-spots
    QJsonArray hotSpots = obj["hot-spots"].toArray();
    for(int i=0;i<hotSpots.count();++i){
        QJsonArray hotSpot = hotSpots[i].toArray();
        this->mPoints.push_back(
            QPointF(hotSpot[0].toDouble(),hotSpot[1].toDouble())
            );
    }
    QJsonArray size = obj["size"].toArray();
    mSize=QSizeF(size[0].toDouble(),size[1].toDouble());
    // ellipses
    QJsonArray ellipses = obj["ellipses"].toArray();
    for(int i=0;i<ellipses.count();++i){
        QJsonArray ellipse = ellipses[i].toArray();
        this->mEllipses.push_back(
            QRectF(
                ellipse[0].toDouble(),ellipse[1].toDouble(),
                ellipse[2].toDouble(),ellipse[3].toDouble()
                )
            );
    }
    return true;
}

bool ElementBase::ElementUiBase::fromFile(const QString &fileName)
{
    QFile file(fileName);
    file.open(QIODeviceBase::ReadOnly);
    if(!file.isOpen()) return false;
    QByteArray json = file.readAll();
    return fromJson(json);
}
